<script setup lang="ts">
import { computed, ref, watch } from 'vue';

import QTableSearch from '@/components/q-table-search/index.vue';
import { FormCreateItem } from '@/types/form-create';
import { defu } from 'defu';
import { VxeGridProps } from 'vxe-table';

import QTable from '../index.vue';
import { PageTableProps, PaginationParams, SearchData } from '../type';
import { useTableHeight } from '../useTableHeight';

defineOptions({
	name: 'QTable',
	inheritAttrs: false,
});

const props = withDefaults(defineProps<PageTableProps>(), {
	showPager: true,
	paginationConfig: () => ({
		defaultPageSize: 10,
		pageSizes: [10, 20, 30, 40, 50],
	}),
});

const paginationParams = ref<PaginationParams>({
	page: 1,
	pageSize: props.paginationConfig?.defaultPageSize ?? 10,
});

const total = ref(0);
const loading = ref(false);

const emit = defineEmits<{
	(e: 'search', values: SearchData): void;
	(e: 'reset', values: SearchData): void;
	(e: 'requested'): void;
	(e: 'operationSelect', data: { key: string; row: any }): void;
}>();

// 表格实例
const qTableRef = ref<InstanceType<typeof QTable>>();
const qTableSearchRef = ref<InstanceType<typeof QTableSearch>>();

// 表格容器引用
const tableContainerRef = ref<HTMLElement>();
// 搜索组件引用
const searchComponentRef = ref<HTMLElement>();
// 分页组件引用
const paginationRef = ref<HTMLElement>();

// 使用表格高度计算组合式函数
const { tableHeight } = useTableHeight({
	containerRef: tableContainerRef,
	searchRef: searchComponentRef,
	paginationRef: paginationRef,
	searchConfig: props.searchConfig,
	paginationConfig: props.paginationConfig,
});

const initGridOptions = reactive<VxeGridProps>({
	columnConfig: {
		resizable: true,
	},
	toolbarConfig: {
		custom: true,
		zoom: true,
		slots: {
			buttons: 'toolbarButtons',
			tools: 'toolbarTools',
		},
	},
});

const searchConfig = computed(() => {
	return {
		...props.searchConfig,
		items: (props.columns
			?.filter((column) => column.searchFormItem)
			.map((column) => ({
				...column.searchFormItem!,
				field: column.searchFormItem?.field ? column.searchFormItem.field : column.field!,
				title: column.searchFormItem?.title ? column.searchFormItem.title : column.title!,
			})) ?? []) as FormCreateItem[],
	};
});

const columns = computed(() => {
	return props.columns?.filter((column) => !column.hiddenInTable) ?? [];
});

const getTableRequestData = () => {
	const searchData = qTableSearchRef.value?.getValues();
	return { ...searchData, ...paginationParams.value };
};

const handleRequest = async (initData?: Record<string, any>) => {
	const postData = getTableRequestData();
	loading.value = true;

	const requestData = { ...postData, ...initData };

	try {
		const res = await props.request?.(requestData);

		if (res?.data) {
			qTableRef.value?.gridRef?.loadData(res.data);
		}
		if (res?.total) {
			total.value = res.total;
		}
		emit('requested');
	} catch (error) {
		console.error(error);
	} finally {
		loading.value = false;
	}
};

const handleSearch = () => {
	if (props.request) {
		handleRequest();
		return;
	}
	const data = getTableRequestData();
	emit('search', data);
};

const handleReset = () => {
	paginationParams.value = {
		page: 1,
		pageSize: props.paginationConfig?.defaultPageSize ?? 10,
	};

	if (props.request) {
		handleRequest();
		return;
	}

	const data = getTableRequestData();

	emit('reset', data);
};

const handlePaginationChange = (value: number, type: 'page' | 'pageSize') => {
	paginationParams.value = { ...paginationParams.value, [type]: value };
	if (props.request) {
		handleRequest();
		return;
	}
	const data = getTableRequestData();
	emit('search', data);
};

onLoad(() => {
	nextTick(() => {
		if (props.searchConfig?.initialValues) {
			qTableSearchRef.value?.setValues(props.searchConfig?.initialValues, 'merge');
		}

		if (qTableSearchRef.value) {
			qTableSearchRef.value.validate((error) => {
				if (!error) {
					handleRequest(props.searchConfig?.initialValues);
				}
			});
		} else {
			handleRequest();
		}
	});
});

const getTableInstance = () => {
	return qTableRef.value?.gridRef;
};

const getTableSearchInstance = () => {
	return qTableSearchRef.value;
};

const refreshTable = async (options?: { expand?: boolean }) => {
	await handleRequest();
	nextTick(() => {
		if (options?.expand) {
			qTableRef.value?.gridRef?.setAllTreeExpand(true);
		}
	});
};

// 导出方法
defineExpose({
	getTableInstance,
	getTableSearchInstance,
	refreshTable,
});
</script>

<template>
	<div ref="tableContainerRef" class="page-wrap space-y-[16px]">
		<div ref="searchComponentRef" v-if="searchConfig.items.length > 0">
			<q-table-search ref="qTableSearchRef" v-bind="searchConfig" @search="handleSearch" @reset="handleReset">
				<template v-for="(slot, name) in $slots" #[name]="scope">
					<slot :name="name" v-bind="scope" />
				</template>
			</q-table-search>
		</div>
		<q-table ref="qTableRef" :columns="columns" :loading="loading" :height="tableHeight" v-bind="defu(initGridOptions, $attrs)">
			<template v-for="(slot, name) in $slots" #[name]="scope">
				<slot :name="name" v-bind="scope" />
			</template>

			<template #toolbarTools>
				<div class=""></div>
			</template>

			<template #loading>
				<n-spin size="small" />
			</template>
			<template v-if="props.operations" #action="{ row }">
				<operation-btns :options="props.operations" :row="row" @select="emit('operationSelect', $event)" />
			</template>
			<template #empty>
				<n-empty description="暂无数据" />
			</template>
		</q-table>
		<div ref="paginationRef" v-if="showPager">
			<n-card class="mt-4" content-class="flex justify-end" size="small">
				<n-pagination
					v-model:page="paginationParams.page"
					v-model:page-size="paginationParams.pageSize"
					:item-count="total"
					show-size-picker
					show-quick-jumper
					v-bind="paginationConfig"
					@update:page="(value) => handlePaginationChange(value, 'page')"
					@update:page-size="(value) => handlePaginationChange(value, 'pageSize')"
				/>
			</n-card>
		</div>
	</div>
</template>

<style scoped lang="scss">
.page-wrap {
	:deep(.vxe-toolbar) {
		padding-top: 0;
		padding-bottom: 16px;
	}
}
</style>
